<script setup lang="ts">
import { ipcRenderer } from 'electron';
import { ref, reactive, watch, onMounted, onUnmounted } from 'vue';
import Store from "electron-store"
import { join } from "node:path"
const store = new Store<Record<string, string>>()
const urls = {
  get clientPack() {
    return store.get('clientPack')
  },
  get updatePacks() {
    return store.get('updatePacks')
  },
  get version() {
    return store.get('version')
  },
}
const hasInstalled = ref(false)
const enum STATUS {
  ready,
  pause,
  doing,
  done,
  unzip,
}
const status = ref(STATUS.ready)
const properties = reactive({
  directory: store.get('directory'),
  overwrite: true,
})
watch(() => properties.directory, async (value) => {
  store.set('directory', value)
  await delay(300)
  onCheck()
})
const onDownload = () => {
  if (properties.directory) {
    setDir(properties.directory)
    ipcRenderer.send('download-button', { url: urls.clientPack, properties: { ...properties } })
  } else {
    alert("请选择下载路径")
  }
}

function sizeTostr(size: number) {
  let data = "";
  if (size < 1024) {
    data = size.toFixed(2) + "B";
  } else if (size < 1024 * 1024) {
    data = (size / 1024).toFixed(2) + "KB";
  } else if (size < 1024 * 1024 * 1024) {
    data = (size / (1024 * 1024)).toFixed(2) + "MB";
  } else {
    data = (size / (1024 * 1024 * 1024)).toFixed(2) + "GB";
  }
  let sizestr = data + "";
  let len = sizestr.indexOf(" .");
  let dec = sizestr.substr(len + 1, 2);
  if (dec == "00") {
    return sizestr.substring(0, len) + sizestr.substr(len + 3, 2);
  }
  return sizestr;
}
const progressInPercentages = reactive({
  percent: '0%',
  transferredBytes: 0,
  speed: 0,
  totalBytes: 0,
})
ipcRenderer.on("download complete", async (event, file) => {
  showTipsFn('下载完成')
  status.value = STATUS.done
  await delay(1000)
  if (file.url === urls.clientPack) {
    showTipsFn('准备解压')
    status.value = STATUS.unzip
    ipcRenderer.send('unzip', { src: file.path, target: store.get('directory'), isClient: true })
    return
  }
  if (file.url === urls.updatePacks) {
    showTipsFn('准备解压')
    status.value = STATUS.unzip
    ipcRenderer.send('unzip', { src: file.path, target: store.get('gameDir') })
    return
  }
});
let lastBytes = 0;
let timer: number;
function countSpeed() {
  timer = setTimeout(() => {
    progressInPercentages.speed = progressInPercentages.transferredBytes - lastBytes;
    lastBytes = progressInPercentages.transferredBytes
    countSpeed()
  }, 1000) as unknown as number
}
onMounted(countSpeed)
onUnmounted(() => {
  clearTimeout(timer);
})

ipcRenderer.on("download progress", (event, progress) => {
  progressInPercentages.percent = (progress.percent * 100).toFixed(2) + '%'
  progressInPercentages.transferredBytes = progress.transferredBytes
  progressInPercentages.totalBytes = progress.totalBytes
});

function clearInfo() {
  status.value = STATUS.ready
  progressInPercentages.percent = '0%'
  progressInPercentages.transferredBytes = 0
  progressInPercentages.totalBytes = 0
}
ipcRenderer.on("download error", (event, error) => {
  showTipsFn('下载出错')
  clearInfo();
});
function setDir(directory: string) {
  store.set("directory", directory)
  store.set('gameDir', join(directory, store.get('packName')))
  store.set('gameExe', join(directory, store.get('packName'), store.get('exeName')))

}
ipcRenderer.on('set path', (event, directory) => {
  properties.directory = directory
  setDir(directory)
})
const openDirectory = () => {
  ipcRenderer.send('open directory', { defaultPath: properties.directory })
}


ipcRenderer.on("download start", (event, error) => {
  showTipsFn('下载开始')
  status.value = STATUS.doing
});
const onDownloadPause = () => {
  showTipsFn('下载暂停')
  status.value = STATUS.pause
  ipcRenderer.send('download pause')
}
const onDownloadResume = () => {
  showTipsFn('下载继续')
  status.value = STATUS.doing
  ipcRenderer.send('download resume')
}

ipcRenderer.on('unzip finish', async (isSuccess, isClient = false) => {
  showTipsFn(isSuccess ? '解压完成' : "解压失败")
  clearInfo()
  if (isClient) {
    await delay(1000)
    onCheck()
  }
})

function onCheck(tips = false) {
  ipcRenderer.send('check version', { tips })
}
onMounted(onCheck)
ipcRenderer.on('has install', () => {
  showTipsFn('游戏已安装')
  hasInstalled.value = true
})

const updateFn = () => {
  ipcRenderer.send('download-button', { url: urls.updatePacks, properties: { ...properties } })
}
ipcRenderer.on('need update', async (event, isNeed) => {
  if (isNeed) {
    showTipsFn('准备自动更新')
    await delay(1000)
    updateFn()
  } else {
    showTipsFn('无需更新，即将启动游戏')
    await delay(1000)
    onStart()
  }
})

function delay(time: number) {
  return new Promise((resolve) => setTimeout(resolve, time))
}
const showTips = ref(false)
const showTipsMsg = ref('')
let showTipsTimer = 0
function showTipsFn(msg: string) {
  clearTimeout(showTipsTimer)
  showTips.value = true
  showTipsMsg.value = msg;
  showTipsTimer = setTimeout(() => {
    showTips.value = false
  }, 2000) as unknown as number
}
const onStart = () => {
  ipcRenderer.send('game start')
}
const showSetting = ref(false)
const settings = reactive({
  dir: "",
  exe: ""
})
const onSelectDir = (type: string) => {
  ipcRenderer.send("select directory", type)
}
const onOpenSetting = () => {
  settings.dir = store.get('gameDir')
  settings.exe = store.get('gameExe')
  showSetting.value = true
}
const onRefactor = async () => {
  if (settings.exe) {
    store.set("gameExe", settings.exe);
    store.set("gameDir", join(settings.exe, ".."));
    store.set("directory", join(settings.exe, "..", ".."));
    showSetting.value = false;
    await delay(300)
    ipcRenderer.send("check version", { force: true })
  } else {
    showTipsFn("请选择启动路径")
  }
}
ipcRenderer.on("select path", (event, { path, type }) => {
  switch (type) {
    case 'dir': settings.dir = path; break;
    case 'exe': settings.exe = path; break;
  }
})
const onReset = () => {
  hasInstalled.value = false
  status.value = STATUS.ready
  ipcRenderer.send("reset store")
}
</script>

<template>
  <template v-if="hasInstalled && [STATUS.done, STATUS.ready].includes(status)">
    <div class="installed">
      <button type="button" @click="onStart()">DNF，启动！</button>
    </div>
  </template>
  <template v-else>
    <div class="card">
      <div style="margin-bottom: 20px;">
        <label for="directory">
          下载路径：
        </label>
        <input id="directory" type="text" :readonly="status !== STATUS.ready" v-model.trim="properties.directory">
        <span role="button" class="dir-btn" @click="openDirectory" v-show="status === STATUS.ready">选择目录</span>
      </div>
      <button type="button" @click="onDownload()" v-show="status === STATUS.ready">开始下载</button>
      <button type="button" @click="onDownloadPause()" v-show="status === STATUS.doing">暂停下载</button>
      <button type="button" @click="onDownloadResume()" v-show="status === STATUS.pause">继续下载</button>
      <button disabled v-show="status === STATUS.unzip">自动解压中请勿关闭</button>
    </div>
    <div class="progress" :style="{ '--percent': progressInPercentages.percent }"
      :class="{ active: status === STATUS.doing }">
      <div>
        下载进度：{{ progressInPercentages.percent }}（{{ sizeTostr(progressInPercentages.speed) }}/S）
      </div>
      <div>
        <span>{{ sizeTostr(progressInPercentages.transferredBytes) }}</span>
        <span>/</span>
        <span>{{ sizeTostr(progressInPercentages.totalBytes) }}</span>
      </div>
    </div>
  </template>
  <div class="mask" v-show="status === STATUS.unzip">
  </div>
  <div class="tips" v-show="showTips">{{ showTipsMsg }}</div>
  <div class="setting" v-show="status === STATUS.ready">
    <span @click="onOpenSetting()">设置</span>
    <span @click="onCheck(true)">检查更新</span>
    <span @click="onReset()">初始化</span>
  </div>
  <div class="setting-content" v-show="showSetting">
    <div>
      <span class="btn m-r-4" @click="onSelectDir('exe')">选择启动路径</span>
      <input type="text" v-model="settings.exe">
    </div>
    <div class="m-t-8">
      <button class="m-r-4" type="button" @click="onRefactor()">确认</button>
      <button type="button" @click="() => showSetting = false">取消</button>
    </div>
  </div>
</template>

<style>
.m-t-8 {
  margin-top: 8px;
}

.m-r-4 {
  margin-right: 4px;
}

:root {
  --bg-color: #ffffffdd;
  --progress-color: #0099ffdd;
}

.dir-btn {
  margin-left: 4px;
  cursor: pointer;
  font-size: 14px;
  color: #0099ff;

}

.dir-btn:hover {
  text-decoration: underline;
}

.dir-btn:active {
  opacity: 0.8;
}

.card {
  background-color: var(--bg-color);
  border-radius: 20px;
}

.progress {
  line-height: 16px;
  font-size: 12px;
  width: 100%;
  border-radius: 100px;
  margin-top: 20px;
  background-image: linear-gradient(to right, var(--progress-color) 0%, var(--progress-color) var(--percent), var(--bg-color) var(--percent), var(--bg-color) 100%);
  padding: 4px 0;
}

.progress.active {
  animation: pulse 1s infinite alternate;
}

@keyframes pulse {
  from {
    box-shadow: 0 0 20px 0 #ffff99 inset, 0 0 10px 0 #ffff99;
  }

  to {
    box-shadow: 0 0 5px 0 #ffffaa inset, 0 0 0px 0 #ffffaa;
  }
}

.mask {
  background-image: url('/unzip.gif');
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  position: absolute;
  right: 10px;
  top: 10px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  box-shadow: 0 0 10px 2px #00ffff inset;
}

.tips {
  position: absolute;
  margin: auto;
  padding: 10px;
  border-radius: 10px;
  background-color: #00000099;
  color: #ffffff;
}

.installed button {
  font-size: 36px;
  font-weight: 700;
}

.installed button:active {
  transform: scale(0.8);
}

.setting {
  position: absolute;
  top: 8px;
  left: 8px;
  font-size: 14px;
  color: #ffff00;
  text-decoration: underline;
  cursor: pointer;
}

.setting span {
  margin-right: 4px;
}

.setting:active {
  opacity: 0.8;
}

.setting-content {
  position: absolute;
  margin: auto;
  padding: 20px;
  border-radius: 20px;
  background-color: #ffffff;
  box-shadow: 0 0 16px 0 #000;
}

.btn {
  color: #0099ff;
  font-size: 14px;
  cursor: pointer;
}

.btn:hover {
  text-decoration: underline;
}

.btn:active {
  opacity: 0.8;
}
</style>
